using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;

using StateMachine_Graphical_Editor.Forms;
using KLATencor.CommonApp.Infrastructure.StateMachine;

namespace StateMachine_Graphical_Editor.Utils.Forms_RunTime_Managment
{
    /// <summary>
    /// Manages the forms in the system. 
    /// <para>Enables creation of new forms, deletion of unnecessary ones, and transitions
    /// <para>between forms in different levels of hierarchy in the display tree.</para>
    /// </para>
    /// </summary>
    public class FormsManager
    {
        #region Members:

        private static FormsManager instance = new FormsManager();

        /// <summary>
        /// Dictionary of all opened forms
        /// <para>Contains pairs of: (stateName),(formDescriptor)</para>
        /// </summary>
        private Dictionary<string, FormDescriptor> formsList;
        
        /// <summary>
        /// Indicator to the current running form
        /// </summary>
        private FormDescriptor top = null;

        #region Constants:

        private const string MAIN = "Root";

        #endregion

        #endregion

        #region Singelton definition:

        //private C'tor
        private FormsManager()
        {
            formsList = new Dictionary<string, FormDescriptor>();
        }

        public static FormsManager getManager()
        {
            return instance;
        }

        #endregion

        #region Getters:

        /// <summary>
        /// checks whether a form is the current active one
        /// </summary>
        /// <param name="sform">The form to be checked</param>
        /// <returns>true if sform is the current form
        /// <para>false otherwise</para>
        /// </returns>
        public bool isTopForm(IFormManagment sform)
        {
            System.Diagnostics.Debug.WriteLine("this is " + this==null? "null": this.ToString());
            System.Diagnostics.Debug.WriteLine("sform is " + sform == null ? "null" : sform.getName());
            System.Diagnostics.Debug.WriteLine("top is " + top == null ? "null" : top.Name);
            System.Diagnostics.Debug.WriteLine("top.Form is " + top.Form == null ? "null" : top.Form.getName());

            return sform.Equals(top.Form);
        }

        #endregion

        #region Public Methods:

        /// <summary>
        /// Called once to initialize the manager with the main form.
        /// </summary>
        /// <param name="StatesForm">the main form</param>
        public void initializeManager(IFormManagment mainForm)
        {
            top = new FormDescriptor(mainForm);
            formsList.Add(MAIN, top);
        }

        /// <summary>
        /// clears the manager from all the forms but the main one
        /// </summary>
        public void resetManager()
        {
            FormDescriptor main = formsList[MAIN] as FormDescriptor;
            foreach (FormDescriptor fd in formsList.Values)
            {
                if (fd.Name.Equals(MAIN))
                    continue;
                fd.close();
            }
            formsList.Clear();
            formsList.Add(MAIN, main);
            top = main;
        }

        /// <summary>
        /// Adds a new form to the forms list, and sets it to be the current form.
        /// </summary>
        /// <param name="item">The form to add</param>
        public void addAndShow(IFormManagment item)
        {
            this.addAndShow(null, item);
        }

        /// <summary>
        /// Adds a new form to the forms list, and sets it to be the current form.
        /// </summary>
        /// <param name="item">The form to add</param>
        /// <param name="transition">The transition causing this form's loading</param>
        public void addAndShow(Transition transition, IFormManagment item)
        {
            //add the form descriptor:
            FormDescriptor fd = new FormDescriptor(item);
            formsList.Add(item.getName(), fd);

            //add to parent's children:
            string parentFDName = getFirstAncestor(item.getState()).Name;

            FormDescriptor fdParent = formsList[parentFDName] as FormDescriptor;
            fd.Parent = fdParent;
            fdParent.addChild(fd);
            fdParent.PermitEventLauching = false;

            //set as top:
            fd.PermitEventLauching = true;
            top = fd;
            //show the new form
            fd.show();
            fd.focus();


            if (transition != null)
            {
                fd.LastTransition = transition;
            }
        }
        /// <summary>
        /// Transfer the activity from one states form to another.
        /// <para>Used when a cross hierarchy transition is performed</para>
        /// </summary>
        /// <param name="transition">The tansition causing the change of forms</param>
        /// <param name="formToLoad">Interface of the form to load now</param>
        public void changeForms(Transition transition, IFormManagment formToLoad)
        {
            State srcParent = transition.Source.Parent;
            State dst = transition.Target;
            this.changeForms(srcParent, dst, transition, formToLoad);
        }

        /// <summary>
        /// Transfer the activity from one states form to another.
        /// <para>Used when a cross hierarchy transition is performed</para>
        /// </summary>
        /// <param name="srcParent">The parent state of the transition's source state</param>
        /// <param name="dst">The transition's destination state</param>
        /// <param name="transition">The tansition causing the change of forms</param>
        /// <param name="formToLoad">Interface of the form to load now</param>
        public void changeForms(State srcParent, State dst, Transition transition, IFormManagment formToLoad)
        {
            //FormDescriptor fdSrc = formsList[srcParent.Name] as FormDescriptor;
            FormDescriptor fdSrc = getSourceDescriptor(srcParent, transition, formToLoad);
            fdSrc.PermitEventLauching = false;

            //check for existing form:
            string dstParentName = dst.Parent != null ? dst.Parent.Name : MAIN;
            
            FormDescriptor fdDst = null;
            if (!formsList.ContainsKey(dstParentName))//form does not exist
            {
                FormDescriptor parentDescriptor = getFirstAncestor(dst);

                fdDst = new FormDescriptor(formToLoad);

                fdDst.Parent = parentDescriptor;
                parentDescriptor.addChild(fdDst);

                formsList.Add(fdDst.Name, fdDst);
            }
            else
            {
                fdDst = formsList[dstParentName] as FormDescriptor;
            }

            fdDst.PermitEventLauching = true;
            top = fdDst;
            FormDescriptor firstCommonAncestor = getFirstCommonAncestor(fdSrc, fdDst);

            clearUnnecessaryChildren(firstCommonAncestor);

            //show the new current form
            fdDst.show();
            fdDst.focus();

            //set on loaded form the state and transition
            fdDst.CurrentState = dst;
            fdDst.LastTransition = transition;
        }

        ///<summary>
        ///returns first common ancestor of both state.
        ///if machine is the common ancestor, returns null;
        ///</summary>
        private FormDescriptor getFirstCommonAncestor(FormDescriptor fd1, FormDescriptor fd2)
        {
            //1. if one is ancestor of the other:
            if (fd1.Find(fd2.Name) != null)
                return fd1;
            if (fd2.Find(fd1.Name) != null)
                return fd2;

            //2. if a parent of one is the common anscestor:
            if (fd1.Parent.Find(fd2.Name) != null)
                return fd1.Parent;
            if (fd2.Parent.Find(fd1.Name) != null)
                return fd2.Parent;

            return getFirstCommonAncestor(fd1.Parent, fd2.Parent);
        }

        #endregion

        #region Private Methods:

        /// <summary>
        /// finds the ancestor form-descriptor that will be the new form-descriptor's parent
        /// </summary>
        /// <param name="dst">the dst state which the form is opened to</param>
        /// <returns>the form-descriptor that should contain as a child the new form-descriptor of dst</returns>
        private FormDescriptor getFirstAncestor(State dst)
        {
            if (dst.Parent == null) //machine
                return formsList[MAIN] as FormDescriptor;//null;
            //direct parent exists in list
            if (formsList.ContainsKey(dst.Parent.Name))
                return formsList[dst.Parent.Name] as FormDescriptor;

            //look for first in hierarchy
            return getFirstAncestor(dst.Parent);
        }

        /// <summary>
        /// Removes all children forms that don't need to be presented anymore
        /// </summary>
        /// <param name="ancestor">the form-descriptor that the unnecessary forms are its offsprings</param>
        private void clearUnnecessaryChildren(FormDescriptor ancestor)
        {
            List<FormDescriptor> offsprings = getOffsprings(ancestor);

            if (!ancestor.Name.Equals(MAIN)) //not the root
            {
                foreach (FormDescriptor fd in offsprings/*ancestor.Children*/)
                {
                    if (fd.Equals(top))
                        continue;
                    fd.close();
                    formsList.Remove(fd.Name);
                }
                ancestor.Children.Clear();

                //return the top descriptor to the children list
                if (top.Parent.Equals(ancestor))
                    ancestor.addChild(top);
            }
            else
            {
                clearAllButMain();
            }
        }

        /// <summary>
        /// Clears the form List except the form-descriptor of the main form
        /// </summary>
        private void clearAllButMain()
        {
            FormDescriptor main = formsList[MAIN] as FormDescriptor;
            foreach (FormDescriptor fd in formsList.Values)
            {
                if (fd.Name.Equals(MAIN) || fd.Equals(top))
                    continue;
                //remove from main's list
                main.Children.Remove(fd);
                fd.close();
            }

            formsList.Clear();
            //return the main descriptor to list
            formsList.Add(MAIN, main);

            if (!formsList.ContainsKey(top.Name)) // main is not the top
                formsList.Add(top.Name, top); 
        }

        /// <summary>
        /// Retrieves all the offsprings of a descriptor
        /// </summary>
        /// <param name="ancestor">The Descriptor to get the offsprings of</param>
        /// <returns>List of FormDescriptorswhich are all offspringof the ancestor descriptor</returns>
        private List<FormDescriptor> getOffsprings(FormDescriptor ancestor)
        {
            List<FormDescriptor> offsprings = new List<FormDescriptor>();
            foreach (FormDescriptor child in ancestor.Children)
            {
                offsprings.Add(child);
                offsprings.AddRange(getOffsprings(child));
            }

            return offsprings;
        }

        private FormDescriptor getSourceDescriptor(State srcParent, Transition transition, IFormManagment formToLoad)
        {
            string srcParentName = srcParent == null ? MAIN : srcParent.Name;
            
            if (formsList.ContainsKey(srcParentName))
                return formsList[srcParentName];

            State src = transition.Source;
            FormDescriptor actualSource = formsList[src.Name]; //exists for sure
            FormDescriptor actualSourceParent = actualSource.Parent;
            
            FormDescriptor fdSrc = new FormDescriptor(formToLoad);

            //Change family dependencies:
            //ancestor of the child is also the ancestor of the child's parent:
            actualSourceParent.addChild(fdSrc);
            fdSrc.Parent = actualSourceParent;
            //set the child parent relationship:
            fdSrc.addChild(actualSource);
            actualSource.Parent = fdSrc;
            //remove child from ancestor's list:
            actualSourceParent.Children.Remove(actualSource);

            formsList.Add(fdSrc.Name, fdSrc);
            return fdSrc;
        }

        #endregion

    }
}
